<template>
    <!-- 预览面板 -->
    <div class="pane">
        <div class="option">
            <el-tooltip content="预览" placement="bottom">
                <span
                    class="el-icon-view"
                    @click="handelOpenDialog('preview')"
                ></span>
            </el-tooltip>
            <el-tooltip content="查看编码" placement="bottom">
                <span
                    class="el-icon-tickets"
                    @click="handelOpenDialog('code')"
                ></span>
            </el-tooltip>
            <el-tooltip :content="isFull ? '缩小' : '全屏'" placement="bottom">
                <span
                    :class="
                        isFull ? 'el-icon-copy-document' : 'el-icon-full-screen'
                    "
                    @click="handelFullScene"
                ></span>
            </el-tooltip>
            <el-tooltip content="保存" placement="bottom">
                <span
                    class="el-icon-folder-add"
                    @click="handelSaveDesign"
                ></span>
            </el-tooltip>
            <el-tooltip content="关闭" placement="bottom">
                <span
                    class="el-icon-switch-button"
                    @click="handelCloseDesign"
                ></span>
            </el-tooltip>
        </div>
        <div class="contener">
            <draggable
                class="list-group"
                :list="layoutRander"
                group="layoutComponent"
                @change="handelChangeLayout"
                :animation="1000"
            >
                <el-form ref="baseForm" class="view-form">
                    <div
                        v-for="(item, index) in layoutRander"
                        :key="index"
                        class="row"
                        :class="activeRow.id === item.id ? 'active' : ''"
                        @click.stop="handelCheckedRow(item)"
                    >
                        <div class="badge">
                            <span
                                class="button"
                                @click.stop="handelDeleteRow(item)"
                            >
                                <svg-icon
                                    icon-class="font-delete-bin-2-fill"
                                    class="font-delete-bin-2-fill"
                                />
                            </span>
                            <span
                                class="button"
                                @click.stop="handelDownRow(item)"
                            >
                                <svg-icon
                                    icon-class="font-arrow-down-line"
                                    class="font-arrow-down-line"
                                />
                            </span>
                            <span
                                class="button"
                                @click.stop="handelUpRow(item)"
                            >
                                <svg-icon
                                    icon-class="font-arrow-up-line"
                                    class="font-arrow-up-line"
                                />
                            </span>
                        </div>
                        <draggable
                            :list="item.value"
                            group="formComponent"
                            @change="handelChangeForm"
                            :animation="1000"
                        >
                            <transition-group>
                                <div
                                    v-for="childItem in item.value"
                                    :key="childItem.id"
                                    class="col"
                                    :class="[
                                        activeCol.id === childItem.id
                                            ? 'active'
                                            : '',
                                        childItem.value.width
                                    ]"
                                    @click.self="handelCheckedCol(childItem)"
                                >
                                    <div class="badge">
                                        <span
                                            class="button"
                                            @click.stop="
                                                handelDeleteCol(childItem)
                                            "
                                        >
                                            <svg-icon
                                                icon-class="font-delete-bin-2-fill"
                                                class="font-delete-bin-2-fill"
                                            />
                                        </span>
                                    </div>
                                    <el-form
                                        :rules="formRules"
                                        :model="formSubmit"
                                        ref="paneForm"
                                    >
                                        <template v-if="childItem.value.ltype">
                                            <template
                                                v-if="
                                                    childItem.value.ltype ===
                                                        'custom'
                                                "
                                            >
                                                <slot
                                                    :name="[
                                                        childItem.value.slotName
                                                    ]"
                                                ></slot>
                                            </template>
                                            <template v-else>
                                                <component
                                                    :is="
                                                        getComponent(
                                                            childItem.value
                                                                .ltype
                                                        )
                                                    "
                                                    :item="childItem.value"
                                                />
                                            </template>
                                        </template>
                                    </el-form>
                                </div>
                            </transition-group>
                        </draggable>
                    </div>
                </el-form>
            </draggable>
        </div>
        <!-- 预览弹框 -->
        <el-dialog
            :title="dialogConfig.title"
            :visible.sync="dialogConfig.show"
            :width="dialogConfig.width"
            v-dialogDrag
            append-to-body
            :close-on-click-modal="false"
            :close-on-press-escape="false"
        >
            <!-- 预览 -->
            <base-form
                :formItems="formItems"
                :formConfig="formConfig"
                :formRules="formRules"
                v-if="dialogConfig.type === 'preview'"
            />
            <!-- 编码查看 -->
            <div class="code-preview" v-if="dialogConfig.type === 'code'">
                <monaco-editor :editorOptions="editorOptions" />
            </div>
            <!-- 新增 -->
            <span slot="footer" class="dialog-footer">
                <el-button size="small" @click="dialogConfig.show = false"
                    >关 闭</el-button
                >
            </span>
        </el-dialog>
    </div>
</template>
<script>
import baseForm from '@/components/baseForm/index.vue'
import MonacoEditor from '@/components/monacoEditor/index'
export default {
    components: {
        baseForm,
        MonacoEditor
    },
    props: {
        designFormSubmit: {
            type: Object,
            default: () => {}
        }
    },
    data() {
        return {
            // 拖拽后的布局列表
            layoutRander: [],
            // 选中的行
            activeRow: {},
            // 生成的表单项配置项
            formItems: [],
            // 生成的表单配置项
            formConfig: {},
            // 生成的表单规则项
            formRules: {},
            // 生成的表单提交项
            formSubmit: {},
            // 选中的列
            activeCol: {},
            // 监听事件
            clickFun: '',
            // 弹框配置
            dialogConfig: {
                title: '',
                show: false,
                width: '60%',
                type: ''
            },
            editorOptions: {
                value: '',
                language: 'json'
            },
            // 是否全屏
            isFull: false
        }
    },
    watch: {
        activeCol(newVal) {
            if (newVal) {
                this.$bus.$emit('ActiveItem', newVal)
            }
        }
    },
    mounted() {
        // 初始化设计器
        this.handelInitDesign()
        this.listenWindowClick()
        // 监听表单配置项
        this.$bus.$on('FormConfig', config => {
            this.formConfig = config
        })
        // 监听规则项变化
        this.$bus.$on('RulesChange', () => {
            this.generateRuleData()
        })
    },
    methods: {
        /**
         * 布局容器拖拽完成
         */
        handelChangeLayout(e) {
            this.layoutRander.forEach((ele, index) => {
                if (ele.id === e.added.element.id) {
                    this.layoutRander.splice(index, 1)
                }
            })
            this.layoutRander.push(e.added.element)
            this.$bus.$emit('ActiveView', this.activeRow)
        },
        /**
         * 表单容器拖拽完成
         */
        handelChangeForm() {
            this.generateFormData()
            this.generateSubmitData()
            this.generateRuleData()
        },
        /**
         * 组装表单项数据结构
         */
        generateFormData() {
            /**
             * 表单拖拽完成后，生成表单配置项
             */
            if (this.layoutRander && this.layoutRander.length > 0) {
                let rows = []
                this.layoutRander.forEach(ele => {
                    let cols = []
                    if (ele.value.length > 0) {
                        ele.value.forEach(element => {
                            cols.push(element.value)
                        })
                    }
                    rows.push(cols)
                })
                this.formItems = rows
            } else {
                this.formItems = []
            }
            this.$forceUpdate()
        },
        /**
         * 组装表单规则项
         */
        generateRuleData() {
            this.formRules = {}
            if (this.layoutRander && this.layoutRander.length > 0) {
                this.layoutRander.forEach(ele => {
                    if (ele.value.length > 0) {
                        ele.value.forEach(element => {
                            this.formRules[element.value.prop] = []
                        })
                    }
                })
                this.layoutRander.forEach(ele => {
                    if (ele.value.length > 0) {
                        ele.value.forEach(element => {
                            if (
                                element.rule &&
                                JSON.stringify(element.rule) !== '{}'
                            ) {
                                for (const key in this.formRules) {
                                    if (
                                        Object.hasOwnProperty.call(
                                            this.formRules,
                                            key
                                        )
                                    ) {
                                        if (element.value.prop === key) {
                                            this.formRules[key] = []
                                            if (element.rule.required) {
                                                let newObj = {
                                                    required: true,
                                                    message:
                                                        element.rule.message,
                                                    trigger: ['blur', 'change']
                                                }
                                                this.formRules[key].push(newObj)
                                            }
                                            if (element.rule.validator) {
                                                let newObj = {
                                                    validator:
                                                        element.rule.validator,
                                                    trigger: ['blur', 'change']
                                                }
                                                this.formRules[key].push(newObj)
                                            }
                                        }
                                    }
                                }
                            } else {
                                return
                            }
                        })
                    }
                })
            } else {
                this.formRules = {}
            }
            this.$forceUpdate()
        },
        /**
         * 组装表单提交项
         */
        generateSubmitData() {
            this.formSubmit = {}
            if (this.layoutRander && this.layoutRander.length > 0) {
                this.layoutRander.forEach(ele => {
                    if (ele.value.length > 0) {
                        ele.value.forEach(element => {
                            if (element.value.propType) {
                                this.$set(
                                    this.formSubmit,
                                    element.value.prop,
                                    element.value.propType
                                )
                            } else {
                                this.$set(
                                    this.formSubmit,
                                    element.value.prop,
                                    ''
                                )
                            }
                        })
                    }
                })
            } else {
                this.formSubmit = {}
            }
            this.$forceUpdate()
        },
        /**
         * 动态获取子表单项
         * @param {String} type
         */
        getComponent(type) {
            return require('@/components/baseForm/components/' + type + '.vue')
                .default
        },
        /**
         * 行选中
         */
        handelCheckedRow(item) {
            if (this.activeRow.id === item.id) {
                this.activeRow = {}
            } else {
                this.activeRow = item
            }
            this.$bus.$emit('ActiveView', this.activeRow)
        },
        /**
         * 行删除
         */
        handelDeleteRow(item) {
            this.$confirm('是否删除当前行?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            })
                .then(() => {
                    this.layoutRander = this.layoutRander.filter(ele => {
                        return ele.id !== item.id
                    })
                    if (this.activeRow.id === item.id) {
                        this.activeRow = {}
                    }
                    this.generateFormData()
                    this.generateRuleData()
                    this.generateSubmitData()
                    this.$message({
                        type: 'success',
                        message: '删除成功!'
                    })
                })
                .catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    })
                })
        },
        /**
         * 行下移
         */
        handelDownRow(item) {
            let index1, index2
            this.layoutRander.forEach((ele, index) => {
                if (item.id === ele.id) {
                    index1 = Number(index)
                    index2 = Number(index + 1)
                }
            })
            this.layoutRander = this.switchRowIndex(
                this.layoutRander,
                index1,
                index2
            )
            this.$forceUpdate()
        },
        /**
         * 行上移
         */
        handelUpRow(item) {
            let index1, index2
            this.layoutRander.forEach((ele, index) => {
                if (item.id === ele.id) {
                    index1 = Number(index)
                    index2 = Number(index - 1)
                }
            })
            if (index2 < 0) {
                return
            } else {
                this.layoutRander = this.switchRowIndex(
                    this.layoutRander,
                    index1,
                    index2
                )
                this.$forceUpdate()
            }
        },
        /**
         * 列选中
         */
        handelCheckedCol(item) {
            if (this.activeCol.id === item.id) {
                this.activeCol = {}
            } else {
                this.activeCol = item
            }
        },
        /**
         * 列删除
         */
        handelDeleteCol(item) {
            this.$confirm('是否删除当前控件?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            })
                .then(() => {
                    this.layoutRander.forEach(ele => {
                        if (ele.value && ele.value.length > 0) {
                            ele.value.forEach((element, index) => {
                                if (element.id === item.id) {
                                    ele.value.splice(index, 1)
                                }
                            })
                        }
                    })
                    if (this.activeCol.id === item.id) {
                        this.activeCol = {}
                    }
                    this.generateFormData()
                    this.generateRuleData()
                    this.generateSubmitData()
                    this.$message({
                        type: 'success',
                        message: '删除成功!'
                    })
                })
                .catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    })
                })
        },
        /**
         * 监听页面点击
         */
        listenWindowClick() {
            this.clickFun = window.addEventListener('click', () => {
                if (JSON.stringify(this.activeRow) !== '{}') {
                    this.activeRow = {}
                } else {
                    return
                }
                this.$bus.$emit('ActiveView', this.activeRow)
            })
        },
        /**
         * 切换行的位置
         */
        switchRowIndex(arr, index1, index2) {
            if (
                index1 < 0 ||
                index1 >= arr.length ||
                index2 < 0 ||
                index2 >= arr.length
            ) {
                return arr
            }
            ;[arr[index1], arr[index2]] = [arr[index2], arr[index1]]
            return arr
        },
        /**
         * 打开弹框
         */
        handelOpenDialog(type) {
            if (type === 'preview') {
                this.generateSubmitData()
                this.generateRuleData()
                this.dialogConfig = {
                    title: '预览',
                    show: true,
                    width: '60%',
                    type: type
                }
            } else if (type === 'code') {
                let rules = {}
                rules = Object.assign(this.formRules)
                for (const key in rules) {
                    if (Object.hasOwnProperty.call(rules, key)) {
                        const element = rules[key]
                        if (element.length > 0) {
                            element.forEach(ele => {
                                if (
                                    ele.validator &&
                                    typeof ele.validator === 'function'
                                ) {
                                    ele.validator = ele.validator.toString()
                                }
                            })
                        }
                    }
                }
                this.editorOptions.value = `{
					"formConfig":${JSON.stringify(this.formConfig)},
					"formItems":${JSON.stringify(this.formItems)},
					"formSubmit":${JSON.stringify(this.formSubmit)},
					"formRules":${JSON.stringify(rules)}}
				`
                this.dialogConfig = {
                    title: '配置项编码',
                    show: true,
                    width: '60%',
                    type: type
                }
            }
        },
        /**
         * 全屏
         */
        handelFullScene() {
            this.isFull = !this.isFull
            this.$emit('FullScene', this.isFull)
        },
        /**
         * 关闭表单设计
         */
        handelCloseDesign() {
            this.$confirm(
                '此操作将退出表单设计, 设计内容不会被保存，是否继续?',
                '提示',
                {
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }
            )
                .then(async () => {
                    this.$router.push({
                        name: 'Form',
                        params: {}
                    })
                })
                .catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    })
                })
        },
        /**
         * 保存表单设计
         */
        handelSaveDesign() {
            let rules = {}
            rules = Object.assign(this.formRules)
            for (const key in rules) {
                if (Object.hasOwnProperty.call(rules, key)) {
                    const element = rules[key]
                    if (element.length > 0) {
                        element.forEach(ele => {
                            if (
                                ele.validator &&
                                typeof ele.validator === 'function'
                            ) {
                                ele.validator = ele.validator.toString()
                            }
                        })
                    }
                }
            }
            let params = {
                formConfig: this.formConfig,
                formItems: this.formItems,
                formSubmit: this.formSubmit,
                formRules: rules
            }
            this.$confirm('保存当前表单?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            })
                .then(async () => {
                    this.$set(
                        this.designFormSubmit,
                        'content',
                        JSON.stringify(params)
                    )
                    this.$set(
                        this.designFormSubmit,
                        'formConfig',
                        JSON.stringify(this.layoutRander)
                    )
                    this.$router.push({
                        name: 'Form',
                        params: {
                            value: this.designFormSubmit
                        }
                    })
                })
                .catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    })
                })
        },
        /**
         * 初始化设计器
         */
        handelInitDesign() {
            console.log('this.designFormSubmit', this.designFormSubmit)
            if (this.designFormSubmit.formConfig) {
                let rander =
                    this.designFormSubmit.formConfig ||
                    JSON.parse(this.designFormSubmit.formConfig)
                this.layoutRander = rander
                this.handelChangeForm()
            } else {
                return
            }
        }
    },
    destroyed() {
        window.removeEventListener('click', this.clickFun)
    }
}
</script>
<style lang="scss" scoped>
.pane {
    @include box_flex(
        $width: 100%,
        $height: 100%,
        $direction: column,
        $justify: flex-start,
        $align: center
    );
    .option {
        @include box_flex(
            $width: 100%,
            $height: 44px,
            $direction: row,
            $justify: flex-end,
            $align: center
        );
        @include border_color_only(
            $direction: bottom,
            $type: solid,
            $width: 1px,
            $color: 'border-base'
        );

        padding: 0 $margin-md;
        font-size: 20px;
        > span {
            cursor: pointer;
            padding: 0 10px;
            transition: all 0.3s;
            &:hover {
                @include font_color($color: 'primary');
            }
        }
    }
    .contener {
        @include box_flex(
            $width: 100%,
            $height: 100%,
            $direction: column,
            $justify: flex-start,
            $align: flex-start
        );

        overflow-y: auto;
        padding: $margin-md;
        .list-group {
            @include box_flex(
                $width: 100%,
                $height: 100%,
                $direction: column,
                $justify: flex-start,
                $align: flex-start
            );
            .row {
                @include border_color($color: 'border-base');

                position: relative;
                margin-bottom: 10px;
                padding: 10px;
                min-height: 65px;
                border-style: dashed;
                cursor: move;
                .badge {
                    @include box_absolute(
                        $width: auto,
                        $height: auto,
                        $bottom: 0%,
                        $right: 0%,
                        $zIndex: -1
                    );
                    @include box_flex(
                        $width: auto,
                        $height: auto,
                        $direction: row,
                        $justify: flex-start,
                        $align: center
                    );

                    opacity: 0;
                    .button {
                        @include box_flex(
                            $width: auto,
                            $height: auto,
                            $direction: row,
                            $justify: center,
                            $align: center
                        );

                        margin: 0 5px;
                        padding: 5px;
                        border-radius: 50%;
                        transition: all 0.3s;
                        cursor: pointer;
                        @include border_color(
                            $type: solid,
                            $color: 'border-base'
                        );
                        @include background_color($color: 'cover1');
                        &:hover {
                            @include border_color(
                                $type: solid,
                                $color: 'danger'
                            );
                            @include font_color($color: 'danger');
                        }
                    }
                }
                > div {
                    @include box_flex(
                        $width: 100%,
                        $height: auto,
                        $direction: row,
                        $justify: flex-start,
                        $align: center,
                        $flex-wrap: wrap
                    );
                    > span {
                        @include box_flex(
                            $width: 100%,
                            $height: auto,
                            $direction: row,
                            $justify: flex-start,
                            $align: center,
                            $flex-wrap: wrap
                        );
                        .col {
                            @include border_color(
                                $type: dashed,
                                $color: 'border-base'
                            );

                            position: relative;
                            padding: 10px;
                            transition: all 0.3s;
                            cursor: move;
                            .badge {
                                @include box_absolute(
                                    $width: auto,
                                    $height: auto,
                                    $bottom: 0%,
                                    $right: 50%,
                                    $zIndex: -1
                                );
                                @include box_flex(
                                    $width: auto,
                                    $height: auto,
                                    $direction: row,
                                    $justify: flex-start,
                                    $align: center
                                );

                                opacity: 0;
                                .button {
                                    @include box_flex(
                                        $width: auto,
                                        $height: auto,
                                        $direction: row,
                                        $justify: center,
                                        $align: center
                                    );

                                    margin: 0 5px;
                                    padding: 5px;
                                    border-radius: 50%;
                                    transition: all 0.3s;
                                    cursor: pointer;
                                    @include border_color(
                                        $type: solid,
                                        $color: 'border-base'
                                    );
                                    @include background_color($color: 'cover1');
                                    &:hover {
                                        @include border_color(
                                            $type: solid,
                                            $color: 'danger'
                                        );
                                        @include font_color($color: 'danger');
                                    }
                                }
                            }
                            &:hover {
                                @include background_color(
                                    $color: 'primary-light2'
                                );
                                .badge {
                                    z-index: 1;
                                    opacity: 1;
                                }
                            }
                        }
                        .col.active {
                            @include border_color(
                                $type: dashed,
                                $color: 'primary'
                            );
                            @include background_color($color: 'primary-light2');
                        }
                    }
                }
                &:hover {
                    @include background_color($color: 'cover1');
                    .badge {
                        z-index: 1;
                        opacity: 1;
                    }
                }
            }
            .row.active {
                @include border_color($type: dashed, $color: 'primary');
                @include background_color($color: 'cover1');
            }
        }
    }
}
.code-preview {
    @include box_flex(
        $width: 100%,
        $height: 60vh,
        $direction: column,
        $justify: flex-start,
        $align: center
    );
}
</style>
